// +FHDR------------------------------------------------------------
//                 Copyright (c) 2023 NOVAUTO.
//                       ALL RIGHTS RESERVED
// -----------------------------------------------------------------
// Filename      : async_fifo_w_ctrl.v
// Author        : ICer
// Created On    : 2023-12-28 11:26
// Last Modified : 2024-01-10 15:38 by ICer
// -----------------------------------------------------------------
// Description:
//
//
// -FHDR------------------------------------------------------------


module async_fifo_w_ctrl #(
    //parameter
    parameter DEPTH = 6
)( /*AUTOARG*/
   // Outputs
   wenc, waddr, waddr_to_read, wfull,
   // Inputs
   clk, rst_n, winc, raddr_sync
   );

// ----------------------------------------------------------------
// Interface declare
// ----------------------------------------------------------------
input                     clk;
input                     rst_n;

//from top
input                     winc;
input [$clog2(DEPTH)   :0]raddr_sync;

//to sram
output                    wenc;
output[$clog2(DEPTH)   :0]waddr;//to reg
output[$clog2(DEPTH)   :0]waddr_to_read;

//to top
output                    wfull;

// ----------------------------------------------------------------
// Wire declare
// ----------------------------------------------------------------
localparam WIDTH = $clog2(DEPTH); //3
localparam WIDTH_EX = WIDTH + 1;

localparam DEPTH_TO2 = 32'b1 << WIDTH; //8
localparam SHIFT     = DEPTH_TO2 - DEPTH; //8 - 6 = 2
localparam HIGH_JUMP = DEPTH_TO2 + SHIFT; //8 + 2 = 10, loop_addr_width = WIDTH_EX

// ----------------------------------------------------------------
// AUTO declare
// ----------------------------------------------------------------
/*AUTOOUTPUT*/
/*AUTOINPUT*/
/*AUTOWIRE*/

// ----------------------------------------------------------------
// waddr loop
// ----------------------------------------------------------------
reg [WIDTH_EX -1:0]waddr_loop_q;
wire[WIDTH_EX -1:0]waddr_loop_d;
wire               waddr_loop_en;

wire               waddr_loop_q_h;
wire [WIDTH   -1:0]waddr_loop_q_l;

assign waddr_loop_d  = (waddr_loop_q == DEPTH - 1'b1) ? HIGH_JUMP : waddr_loop_q + 1'b1;
assign waddr_loop_en = winc;

assign waddr_loop_q_h = waddr_loop_q[WIDTH];
assign waddr_loop_q_l = waddr_loop_q[WIDTH-1:0];

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    waddr_loop_q <= {WIDTH_EX{1'b0}};
  end 
  else if(waddr_loop_en) begin
    waddr_loop_q <= waddr_loop_d;
  end
end

// ----------------------------------------------------------------
// cnt logic
// ----------------------------------------------------------------
wire [WIDTH_EX -1:0]fifo_cnt;
wire [WIDTH_EX -1:0]real_waddr_ex;
wire [WIDTH_EX -1:0]real_raddr_ex;

wire               raddr_loop_q_h;
wire [WIDTH   -1:0]raddr_loop_q_l;

wire [WIDTH   -1:0]waddr_loop_q_l_offset;
wire [WIDTH   -1:0]raddr_loop_q_l_offset;

assign raddr_loop_q_h = raddr_sync[WIDTH];
assign raddr_loop_q_l = raddr_sync[WIDTH-1:0];

assign waddr_loop_q_l_offset = (waddr_loop_q_l - SHIFT);
assign raddr_loop_q_l_offset = (raddr_loop_q_l - SHIFT);

assign real_waddr_ex = waddr_loop_q_h ? {waddr_loop_q_h, waddr_loop_q_l_offset} : waddr_loop_q;
assign real_raddr_ex = raddr_loop_q_h ? {raddr_loop_q_h, raddr_loop_q_l_offset} : raddr_sync;

assign fifo_cnt      = (real_waddr_ex[WIDTH] == real_raddr_ex[WIDTH]) ? real_waddr_ex[WIDTH-1:0] - real_raddr_ex[WIDTH-1:0]:
                                                                        DEPTH + real_waddr_ex[WIDTH-1:0] - real_raddr_ex[WIDTH-1:0];

// ----------------------------------------------------------------
// out logic
// ----------------------------------------------------------------
assign wenc          = winc && (!wfull);
assign wfull         = fifo_cnt >= DEPTH;
assign waddr         = real_waddr_ex;
assign waddr_to_read = waddr_loop_q;

endmodule
// Local Variables:
// verilog-auto-inst-param-value:t
// verilog-library-directories:(".")
// verilog-library-extensions:(".v")
// End:

